home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
drdobbs
/
1987
/
09
/
holublst.sep
< prev
next >
Wrap
Text File
|
1987-08-13
|
38KB
|
1,033 lines
; Play four measures of 3/4, followed by four measures of a 3/4
; against 4/4 polyrhythm, followed by four measures of 3 against
; 4 against 5, followed by four measures of 3 against 4 against 5
; against 7. Sound a warning tone one measure before each change.
track 0: #4 3/4 @120 w, #4 3/4 @120 w, #4 3/4 @120 w, #4 3/4 @120 w
track 1: (#4 3/4) , #4 4/4 , #4 4/4 , #4 4/4
track 2: (#4 3/4) , (#4 5/4) , #4 5/4 , #4 5/4
track 3: (#4 3/4) , (#4 7/4) , (#4 7/4) , #4 7/4
; Now go into Steve Reich mode. Play 20 measures of 4/4 at
; metronome 150 and at the same time play 20 measures of 4/4
; at metronome 151. The clicks start out on the same
; beat and they very gradually go out of phase and then
; come back into phase again.
track 0: #20 4/4 @ 150
track 1: #20 4/4 @ 151
Example 1: A click program
Listing 1 -- debug.h
____________________________________________________________________________
1| #define PUBLIC
2|
3| #ifdef DEBUG
4| # define PRIVATE
5| # define D(x) x
6| #else
7| # define PRIVATE static
8| # define D(x)
9| #endif
10|
11| #ifdef MSDOS
12| # define MS(x) x
13| # define UX(x)
14| #else
15| # define MS(x)
16| # define UX(x) x
17| #endif
Listing 2 -- hardware.h
____________________________________________________________________________
1| /* Defines for IBM-PC Hardware */
2|
3| /* Timer defines:
4| *
5| * TIMR_CLK The number of system clock cycles in a second
6| * (ie. the input frequency of the counter/timer).
7| * TIMR_TICKS(d) The number of ticks needed to get a delay of d
8| * seconds. 'd' can be a fraction: TIMR_TICKS(.5).
9| *
10| * TIMR_CTRL Address of control port
11| * TIMR_2_DATA Address of counter 2 data port
12| * TIMR_2_LOAD Control word to load new count into
13| * timer 2 (the speaker--2 bytes, lsb first).
14| * Timer initilized in mode 3 (square wave)
15| * TIMR_0_LOAD Same but for timer 0 (system clock).
16| * TIMR_0_DATA
17| */
18|
19| #define TIMR_CLK 1193180L
20| #define TIMR_TICKS(d) (int)((double)(d) * (TIMR_CLK/65536.0))
21|
22| #define TIMR_CTRL 0x43
23|
24| #define TIMR_0_DATA 0x40
25| #define TIMR_0_LOAD 0x36
26|
27| #define TIMR_2_DATA 0x42
28| #define TIMR_2_LOAD 0xb6
29|
30| /* Programmable peripheral interface:
31| *
32| * PPI Base address of interface
33| * PPI_SPKR Bit mask to enable speaker (bit 0 is
34| * gate on timer chip, bit 1 actually
35| * enables the speaker).
36| */
37|
38| #define PPI 0x61
39| #define PPI_SPKR 0x03
40|
41| /*------------------------------------------------------------
42| * Make the speaker beep at a particular frequency:
43| *
44| * SETFRQ(freq); Sets the frequency, freq can be floating
45| * point
46| * SPKR_ON(); Turns the speaker on.
47| * SPKR_OFF(); Turns it off again.
48| */
49|
50| #define SETFRQ( freq )
51| if( 1 )
52| {
53| unsigned int count;
54|
55| count = TIMR_CLK / freq ;
56|
57| outp( TIMR_CTRL , TIMR_2_LOAD );
58| outp( TIMR_2_DATA, count & 0xff );
59| outp( TIMR_2_DATA, (count >> 8) & 0xff );
60| }
61| else
62|
63| #define SPKR_ON() outp( PPI, inp(PPI) | PPI_SPKR )
64| #define SPKR_OFF() outp( PPI, inp(PPI) & ~PPI_SPKR )
Listing 3 -- notes.h
____________________________________________________________________________
1| /* These #defines are the frequencies 12 notes of the octave
2| * starting with middle C. Multiply by two to go up an octave,
3| * divide by two to go down. This is an equal-tempered scale
4| * so each note is derrived by multiplying the previous note
5| * by the twelfth root of two. Note that there's a little
6| * round-off error here but this error isn't audible.
7| */
8|
9| #define TWELFTH_ROOT_OF_TWO 1.059463095
10|
11| #define C4 (261.6256) /* C 4 */
12| #define C4_SHARP (277.1826) /* C# 4 */
13| #define D4 (293.6648) /* D 4 */
14| #define D4_SHARP (311.1270) /* D# 4 */
15| #define E4 (329.6276) /* E 4 */
16| #define F4 (349.2282) /* F 4 */
17| #define F4_SHARP (369.9944) /* F# 4 */
18| #define G4 (391.9954) /* G 4 */
19| #define G4_SHARP (415.3047) /* G# 4 */
20| #define A4 (440.0000) /* A 4 */
21| #define A4_SHARP (466.1638) /* A# 4 */
22| #define B4 (493.8833) /* B 4 */
Listing 4 -- beep.c
____________________________________________________________________________
1| #include <stdio.h>
2| #include <tools/hardware.h>
3|
4| beep( freq, duration )
5| double freq;
6| double duration;
7| {
8| /* Beep the bell on the IBM-PC for the indicated time
9| * (which may be fractional) at the indicated frequency.
10| * Frequencies for various notes in an equal-tempered
11| * scale are in <tools/notes.h>.
12| */
13|
14| SETFRQ( freq );
15|
16| SPKR_ON();
17|
18| delay( (double)duration );
19|
20| SPKR_OFF();
21| }
22|
23| /*------------------------------------------------------------*/
24| #ifdef MAIN
25| #include <tools/notes.h>
26|
27| main( argc , argv )
28| char **argv;
29| {
30| double atof();
31|
32| # if 0
33| beep( C * 4 , atof(argv[1]) );
34| # endif
35|
36| beep( C , 0.5 );
37| beep( D , 0.5 );
38| beep( E , 0.5 );
39| beep( F , 0.5 );
40| beep( G , 0.5 );
41| beep( A , 0.5 );
42| beep( B , 0.5 );
43| beep( C * 2 , 0.5 );
44| }
45| #endif
Listing 5 -- delay.c
____________________________________________________________________________
1| #include <tools/hardware.h>
2| #include <dos.h>
3|
4| delay( duration )
5| double duration;
6| {
7| /* Delay for the indicated number of seconds (may be
8| * fractional.
9| */
10|
11| unsigned long start, elapsed ;
12|
13| unsigned long i, t;
14|
15| elapsed = TIMR_TICKS( duration );
16|
17| for( start = ticks(); ticks() - start < elapsed ;)
18| ;
19| }
20|
21| ticks()
22| {
23| /* Return the number of BIOS clock ticks since midnight.
24| * The routine rolls over succesfully at midnight (1573040
25| * is the number of clock ticks in a day and AL is zero
26| * if the timer has not passed midnight since the last
27| * call).
28| */
29|
30| union REGS regs;
31|
32| regs.h.ah = 0;
33|
34| int86( 0x1a, ®s, ®s ); /* Time-of-day interrupt */
35|
36| return ( regs.h.al ? 1573040L : 0 )
37| + ( regs.x.cx << 16 )
38| + regs.x.dx
39| ;
40| }
41|
42| #ifdef MAIN
43| main()
44| {
45| printf("Should be five seconds between beeps\n\007");
46| delay( 5.0 );
47| printf("\007");
48| }
49| #endif
Listing 6 -- speedup.asm, Printed 5/25/1987
____________________________________________________________________________
1| PAGE 56,132
2| TITLE SPEEDUP.ASM: System-clock-modification routines
3| ;------------------------------------------------------------
4| DEBUG equ 1 ; Set to 1 to make internal symbols public
5| ;------------------------------------------------------------
6|
7| _TEXT SEGMENT BYTE PUBLIC 'CODE'
8| _TEXT ENDS
9| _DATA SEGMENT WORD PUBLIC 'DATA'
10| _DATA ENDS
11| CONST SEGMENT WORD PUBLIC 'CONST'
12| CONST ENDS
13| _BSS SEGMENT WORD PUBLIC 'BSS'
14| _BSS ENDS
15| DGROUP GROUP CONST, _BSS, _DATA
16| ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
17|
18| EXTRN __chkstk:NEAR
19|
20| ;------------------------------------------------------------
21|
22| TIMR_CTRL = 43H ; address of timer control port
23| TIMR_0_DATA = 40H ; address of counter 0 data port
24| TIMR_0_LOAD = 36H ; control word for timer
25|
26| ;------------------------------------------------------------
27|
28| _TEXT SEGMENT
29|
30| ;------------------------------------------------------------
31| ; Misc. variables. Note that I'm putting all these in the
32| ; code (_TEXT) segment so that I can find them when an
33| ; interrupt comes along. The PUBLIC statements are just for
34| ; debugging.
35|
36| old_int equ $
37| old_off dw ? ; Offset of old timer interrupt
38| ; service routine.
39| old_seg dw ? ; Segment address of same.
40|
41| service dw ? ; User-supplied interrupt service
42| ; routine (offset).
43|
44| old_ds dw ? ; segments for running program.
45| tick_reset dw ?
46| numticks dw ? ; Initialized to tick_reset, decre-
47| ; mented on each timer interrupt,
48| ; reset to the speedup factor (and the
49| ; old service routine is called) when
50| ; it reaches zero.
51|
52| stack dw 64 dup (0) ; Local stack for service routine
53| ; 18 bytes are used by real service
54| ; routine, the rest is available
55| ; for the user service routine.
56| stack_end dw ?
57| old_sp dw ?
58| old_ss dw ?
59|
60| IF DEBUG
61| PUBLIC old_int, old_off, old_seg, old_ds
62| PUBLIC service, tick_reset, numticks, serv
63| ENDIF
64|
65| ;------------------------------------------------------------
66| ; cli(); sti(); Disable and enable interrupts.
67| ;
68| PUBLIC _cli, _sti
69|
70| _cli PROC NEAR
71| cli
72| ret
73| _cli ENDP
74|
75| _sti PROC NEAR
76| sti
77| ret
78| _sti ENDP
79|
80| ;------------------------------------------------------------
81| ; speedup( factor, routine )
82| ; int factor, (*routine)();
83| ;
84| ; Speed up the system clock by the indicated factor.
85| ; Call the indicated subroutine on every timer interrupt.
86| ; and call the default clock routine as well every "factor"
87| ; ticks.
88| ;
89| ; Offsets to arguments:
90| ; factor = [bp+4]
91| ; routine = [bp+6]
92| ;
93| PUBLIC _speedup
94|
95| _speedup PROC NEAR
96| push bp
97| mov bp,sp
98| xor ax,ax
99| call __chkstk
100|
101| mov ax,[bp+6] ; service = offset of new
102| mov _TEXT:service,ax ; routine.
103| mov _TEXT:old_ds,ds ; remember current DS too.
104| mov ax,[bp+4] ; tick_reset = numticks
105| mov _TEXT:tick_reset,ax ; = ax = factor
106| mov _TEXT:numticks,ax ;
107|
108| mov al,TIMR_0_LOAD ; Set up timer for load
109| out TIMR_CTRL,al ;
110| mov ax,[bp+4] ; if( factor == 1 )
111| cmp ax,01H ; {
112| jne do_div ; use 0 for the ouput count
113| mov ax,0 ; }
114| jmp load ; else
115| do_div: ; {
116| mov ax,00000H ; Number of ticks =
117| mov dx,00001H ; 65536/factor
118| mov bx,[bp+4] ; BX = factor.
119| div bx ; AX = number of ticks
120| load: ; }
121| out TIMR_0_DATA,al ; Send new count to timer
122| mov al,ah ;
123| out TIMR_0_DATA,al ;
124|
125| ; Get the old vector
126| mov ah,35H ;
127| mov al,08H ;
128| int 21H ;
129| mov _TEXT:old_off,bx ;
130| mov _TEXT:old_seg,es ;
131|
132| ; set up the new vector
133| mov ah,25H ;
134| mov al,08H ;
135| mov dx,OFFSET _TEXT: serv ;
136| push ds ;
137| push cs ;
138| pop ds ;
139| int 21H ;
140| pop ds ;
141|
142| mov sp,bp
143| pop bp
144| ret
145|
146| _speedup ENDP
147|
148| ;------------------------------------------------------------
149| ; Actual interrupt service routine. This routine saves the
150| ; environment, calles the user-supplied C service routine,
151| ; and then calls the default service routine if necessary.
152| ; The service routine runs under its own stack so stack
153| ; checking should be disabled with either the /Gs command-
154| ; line switch or the "#pragma check_stack[+|-]" directive.
155| ;
156| serv PROC NEAR
157|
158| push ax ; Save AX on old stack
159|
160| mov _TEXT:old_sp,sp ; Set up local
161| mov _TEXT:old_ss,ss ; stack
162| push cs ;
163| pop ss ;
164| mov sp,offset _TEXT: stack_end ;
165|
166| push bx ; Set up C environment:
167| push cx ; save everything (the
168| push dx ; flags are saved as
169| push bp ; part of the interrupt
170| push si ; processing).
171| push di ;
172| push ds ;
173| push es ;
174| ;
175| mov ds,_TEXT:old_ds ; fix the data segment
176| mov es,_TEXT:old_ds ; and extra segment
177|
178| cli
179| call word ptr _TEXT:service ; Call C subroutine
180| sti
181| ;
182| pop es ; restore everything
183| pop ds ; but AX
184| pop di ;
185| pop si ;
186| pop bp ;
187| pop dx ;
188| pop cx ;
189| pop bx ;
190|
191| mov ss,_TEXT:old_ss ; Restore original
192| mov sp,_TEXT:old_sp ; stack.
193| ;
194| dec _TEXT:numticks ; if(--numticks > 0)
195| jle do_old_int ; {
196| mov al,20h ; send EOI
197| out 20h,al ;
198| pop ax ; restore ax
199| iret ; }
200| ; else
201| do_old_int: ; {
202| mov ax,_TEXT:tick_reset ; numticks
203| mov _TEXT:numticks,ax ; = tick_reset;
204| pop ax ; restore ax
205| jmp dword ptr _TEXT:old_int ; jmp to old vector
206| ; }
207| serv ENDP
208|
209| ;------------------------------------------------------------
210|
211| PUBLIC _slowdown
212|
213| _slowdown PROC NEAR
214|
215| push bp
216| mov bp,sp
217| xor ax,ax
218| call __chkstk
219|
220| mov ax,_TEXT:old_off ; See if the interrupts have
221| or ax,ax ; changed.
222| jz no_int ; No, don't fix them then
223|
224| ; restore old timer interrupt
225| push ds ;
226| mov ah,25H ;
227| mov al,08H ;
228| mov ds,_TEXT:old_seg ;
229| mov dx,_TEXT:old_off ;
230| int 21H ;
231| pop ds ;
232|
233| no_int:
234| mov al,TIMR_0_LOAD ; Restore default system
235| out TIMR_CTRL,al ; clock tick rate
236| mov al,0
237| out TIMR_0_DATA,al
238| out TIMR_0_DATA,al
239|
240| mov sp,bp
241| pop bp
242| ret
243|
244| _slowdown ENDP
245|
246| _TEXT ENDS
247| END
Listing 7 -- click.c
____________________________________________________________________________
1| #include <stdio.h>
2| #include <signal.h>
3| #include <stdarg.h>
4| #include <ctype.h>
5| #include <tools/notes.h> /* Frequencies of notes */
6| #include <tools/hardware.h> /* TIMR_CLK */
7| #include <tools/debug.h> /* D() macro */
8|
9| /* CLICK.C A polyrhythmic metronome. Usage is described
10| * in the usage_msg[], below.
11| *
12| * (c) 1987, Allen I. Holub. All rights reserved.
13| *------------------------------------------------------------
14| */
15|
16| extern double ceil ( double );
17| extern double floor( double );
18|
19| /*------------------------------------------------------------
20| * The compiler truncates floating point numbers when they're
21| * converted to int. This macro rounds as it converts.
22| */
23|
24| #define ROUND(x) ((int)( ((x) > 0.5) ? ceil ((double)(x)) \
25| : floor((double)(x)) ) )
26|
27| /*------------------------------------------------------------
28| * TICKS(x) converts a metronome count to clock ticks.
29| *
30| * With a speedup factor 4, a tick happens 72.84 times/second
31| * (every .01373 seconds, more or less). A speedup factor of
32| * 2 yields half this number: 36.42 times/sec, or an interrupt
33| * every .02746 seconds).
34| *
35| * A metronome 60 is 1 Hz, 120 is 2 Hz, etc.
36| *
37| * seconds == 60 / metronome_count
38| * ticks in second == ( 60 / metronome_count ) * ONE_TICK
39| * == ( (60 * ONE_TICK) / metronome_count )
40| */
41|
42| #define FACTOR 16 /* Speedup factor */
43| #define DEFAULT_TICK (TIMR_CLK / 65536.0) /* ticks / second */
44| #define ONE_TICK (DEFAULT_TICK * FACTOR)
45|
46| #define TICKS(x) ROUND( (60.0 * ONE_TICK) / (x) )
47|
48| /*------------------------------------------------------------*/
49|
50| #define min(a,b) ((a) < (b) ? (a) : (b))
51| #define max(a,b) ((a) > (b) ? (a) : (b))
52|
53| #define MAX_MEASURES 1280 /* Max # of measures/track */
54| #define NUM_TRACKS 4 /* Number of tracks */
55|
56| #define WARNING (NUM_TRACKS + 1)
57|
58| /*------------------------------------------------------------*/
59|
60| typedef unsigned char uchar;
61| typedef unsigned int uint;
62|
63| typedef struct
64| {
65| uchar num_beats; /* # of beats remaining in measure */
66| uchar remainder; /* Number of ticks to get in synch */
67| uint cur_tick; /* Current tick for this beat */
68| uint ticks_per_beat ; /* # of clock ticks between beats */
69| uint metronome : 14 ; /* metronome count in this measure */
70| uint silent : 1 ; /* this measure is silent */
71| uint warning : 1 ; /* warning tone at downbeat */
72| }
73| MEASURE;
74|
75| typedef MEASURE TRACK [ MAX_MEASURES ];
76|
77| TRACK Tape [ NUM_TRACKS ];
78| MEASURE *Measure [ NUM_TRACKS ]; /* Current measure on */
79| /* each track of Tape.*/
80| int Lineno = 0; /* Input line number */
81|
82| int Ring_bell = 0; /* 0 if the bell shouldn't ring.
83| * Set to 1 for track 1, 2 for track
84| * 2, etc.
85| */
86|
87| int Collision = 0 ; /* If two track collide, the track
88| * number of the second one is
89| * put here.
90| */
91|
92| int Downbeat = 1; /* Incremented by the interrupt
93| * service routine on every
94| * downbeat from track 0;
95| */
96|
97| int Done = 0; /* Set to 1 by interrupt service
98| * routine when it gets to the
99| * end of the tape.
100| */
101|
102| int Numticks = 0; /* For debugging, incremented on
103| * every timer interrupt.
104| */
105|
106| /*------------------------------------------------------------*/
107|
108| char *Usage_msg[] =
109| {
110| "Usage: click [-d] inputfile",
111| "",
112| " -d (for dull) don't use different notes for different",
113| " tracks",
114| "",
115| "A polyrhythmic metronome. Four independent \"tracks\"",
116| "are supported, with rhythms specified as follows:",
117| "",
118| "track 0: #4 @120 5/8, #6 @120 6/8, @120 4/4",
119| "track 1: #4 6 , #6 @100 6/8",
120| "",
121| "No line can be longer than 132 characters, but several",
122| "track specifiers can be given. The basic notation is:",
123| "\"[#N] [@Z] X[/Y],\" interpreted as N measures of X/Y at",
124| "metronome Z. If the \"#N\" is missing, 1 is used. If",
125| "the \"@Z\" is missing, the measure is stretched to",
126| "synch with track one on the down beat. The \"/Y\" is",
127| "optional. So, in the above example, the six beats in",
128| "the first four measures of track 2 will synch up with",
129| "track one, coming into synch on the downbeat of each",
130| "measure. Each track may be up to 1000 measures long.",
131| "Lines that don't begin with \"track\" are ignored",
132| NULL
133| };
134|
135| /*------------------------------------------------------------*/
136|
137| char *skipwhite(p)
138| char *p;
139| {
140| /* Skip all characters that aren't part of a command */
141|
142| while( *p && (isspace(*p) || *p == '\n') )
143| p++ ;
144|
145| return p;
146| }
147|
148| /*------------------------------------------------------------*/
149|
150| err( fmt )
151| char *fmt;
152| {
153| /* Works like printf() but writes to standard error and
154| * prints an input line number along with the message.
155| */
156|
157| va_list args;
158| va_start( args, fmt );
159|
160| fprintf ( stderr, "line %d: ", Lineno );
161| vfprintf( stderr, fmt, args );
162| }
163|
164| /*------------------------------------------------------------*/
165|
166| init()
167| {
168| /* Initialize The Measure array to point
169| * at the first measure of each track on the Tape.
170| */
171|
172| register int i;
173|
174| for( i = NUM_TRACKS; --i >= 0; )
175| Measure[i] = Tape[i] ;
176| }
177|
178| /*------------------------------------------------------------*/
179|
180| print_tape( this_many )
181| {
182| /* Print out the tape. If this_many is 0, only the
183| * initialized measures are printed; otherwise, the
184| * indicated number of measures are printed
185| */
186|
187| MEASURE *p ;
188| register int i , measure_num ;
189| int amt;
190|
191| for( i = 0; i < NUM_TRACKS ; i++ )
192| {
193| printf( "Track %d:\n", i );
194| measure_num = 0;
195| amt = this_many;
196|
197| for( p=Tape[i]; p->num_beats>0 || --amt >= 0; p++)
198| {
199| printf(" measure %2d: ", ++measure_num );
200| printf("[%2d ticks/beat " , p->ticks_per_beat );
201| printf("+ %d], " , p->remainder );
202| printf("cur_tick=%d, " , p->cur_tick );
203| printf( "%d beats " , p->num_beats );
204|
205| if( p->metronome )
206| printf( "at %-5d ", p->metronome );
207| else
208| printf( "in synch " );
209|
210| printf("%s", p->silent ? "(mute)" : "" );
211| printf("%s", p->warning ? "(warn)" : "" );
212| printf("\n");
213| }
214| }
215| }
216|
217| /*------------------------------------------------------------*/
218|
219| build_tracks( file_name )
220| char *file_name;
221| {
222| FILE *fp;
223| MEASURE *mp;
224| char buf[133], *line;
225| int i;
226| int track; /* Track number */
227| int num_measures; /* # of measures to repeat */
228| int metronome; /* metronome count */
229| int beats; /* beats per measure */
230| int ticks; /* ticks per measure */
231| int measure; /* Current measure number */
232| int silent; /* measure is silent */
233| int warning; /* warning on last repeat */
234|
235| if( ! (fp = fopen(file_name,"r")) )
236| return 0;
237|
238| init();
239|
240| while( line = fgets(buf,133,fp) )
241| {
242| ++Lineno;
243|
244| line = skipwhite( line );
245|
246| if( !( line[0]=='t' && line[1]=='r'
247| && line[2]=='a' && line[3]=='c' && line[4]=='k'
248| )
249| ) continue;
250|
251| line += 5; /* Get track number */
252| line = skipwhite ( line );
253| track = stoi ( &line );
254| line = skipwhite ( line );
255|
256| if( *line == ',' || *line == ':' )
257| line++;
258|
259| mp = Measure [ track ]; /* starting measure # */
260| measure = mp - Tape [ track ];
261|
262| while( *line )
263| {
264| num_measures = 1;
265| metronome = 0;
266| silent = 0;
267| warning = 0;
268|
269| for( line=skipwhite(line); *line; line=skipwhite(line) )
270| {
271| if( *line == ';' ) /* comment */
272| {
273| *line = 0;
274| break;
275| }
276| else if( *line == ',' || *line == ':' )
277| {
278| ++line; /* end of measure */
279| break;
280| }
281| if( *line == '#' ) /* # of measures */
282| {
283| line = skipwhite ( ++line );
284| num_measures = stoi ( &line );
285| }
286| else if( *line == '@' ) /* metronome count */
287| {
288| line = skipwhite ( ++line );
289| metronome = stoi ( &line );
290| }
291| else if( *line == 'w' || *line == 'W' )
292| {
293| while( isalpha(*line ) )
294| ++line;
295|
296| warning = 1;
297| }
298| else if( isdigit( *line ) ) /* Time signature */
299| {
300| if( (beats = stoi(&line)) == 0 )
301| {
302| err( "Illegal time signature\n" );
303| exit(1);
304| }
305|
306| if( *line == '/' ) /* Throw away bottom */
307| ++line; /* of time signature. */
308|
309| while( isdigit( *line ) )
310| line++;
311| }
312| else if( *line == '(' || *line == ')' )
313| {
314| ++ line ;
315| silent = 1;
316| }
317| else
318| err("<%c> is illegal in measure spec.\n", *line );
319| }
320|
321| for(; --num_measures >= 0; mp++, measure++ )
322| {
323| if( metronome )
324| {
325| mp->metronome = metronome;
326| ticks = TICKS(metronome) * beats ;
327| }
328| else
329| {
330| mp->metronome = 0;
331| ticks = Tape[0][measure].ticks_per_beat
332| * Tape[0][measure].num_beats
333| ;
334| }
335|
336| if( num_measures == 0 ) /* last in series */
337| mp->warning = warning;
338|
339| mp->silent = silent ;
340| mp->num_beats = beats ;
341| mp->cur_tick = ticks / beats;
342| mp->ticks_per_beat = ticks / beats;
343| mp->remainder = ticks % beats;
344|
345| D( printf("loading track %d, ", track );)
346| D( printf("measure %d: " , measure );)
347| D( printf("%d beats/measure " , beats );)
348| D( printf("at metronome %d\n" , mp->metronome);)
349| }
350|
351| Measure[ track ] = mp ;
352| }
353| }
354|
355| init();
356| D( print_tape( 0 ); )
357| return 1;
358| }
359|
360| /*------------------------------------------------------------*/
361|
362| #pragma check_stack- /* Turn off stack probes. This pragma */
363| /* is Microsoft-compiler dependent. */
364|
365| timer_intr()
366| {
367| /* Interrupt service routine for timer interrupt */
368|
369| MEASURE **mp, *p ;
370| int i, did_nothing ;
371|
372| Done = 1;
373| ++ Numticks;
374|
375| for( i = 0, mp = Measure; ++i <= NUM_TRACKS ; mp++ )
376| {
377| if( (p = *mp)->num_beats > 0 )
378| {
379| if( p->cur_tick==p->ticks_per_beat )
380| {
381| /* Ring bell on first tick of measure
382| * unless this is a silent measure.
383| * Warnings take precedence over
384| * everything.
385| */
386|
387| if( p->warning )
388| {
389| Ring_bell = WARNING ;
390| p->warning = 0;
391| }
392|
393| else if( !p->silent )
394| {
395| if( !Ring_bell )
396| Ring_bell = i;
397| else
398| Collision = i;
399| }
400| }
401|
402| if( -- p->cur_tick <= 0 )
403| {
404| if( -- p->num_beats <= 0 )
405| {
406| if( i == 1 )
407| ++Downbeat;
408|
409| ++( *mp ); /* go to next measure */
410| }
411| else
412| {
413| p->cur_tick = p->ticks_per_beat;
414|
415| if( p->remainder > 0 )
416| {
417| -- p->remainder ;
418| ++ p->cur_tick ;
419| }
420| }
421| }
422| Done = 0;
423| }
424| }
425| }
426|
427| #pragma check_stack+
428|
429| /*------------------------------------------------------------*/
430|
431| on_break()
432| {
433| /* Routine for signal(), tries to put the clock rate
434| * back to normal on a Ctrl-Break. Note that this
435| * routine can fail if Ctrl-Break is hit several times
436| * in quick succession.
437| */
438|
439| signal( SIGINT, SIG_IGN );
440| slowdown();
441| exit(0);
442| }
443|
444| /*------------------------------------------------------------*/
445|
446| print_stats()
447| {
448| }
449|
450| /*------------------------------------------------------------*/
451|
452| usage()
453| {
454| char **p;
455| for( p = Usage_msg; *p; fprintf(stderr,"%s\n", *p++) )
456| ;
457| exit(1);
458| }
459|
460| /*------------------------------------------------------------*/
461|
462| main( argc, argv )
463| char **argv;
464| {
465| static int measure = 0; /* current measure in track 0 */
466| static int dull = 0; /* Dull output */
467| static int stats = 0; /* Statistics only */
468| static int i;
469|
470| if( argc == 3 )
471| {
472| --argc;
473|
474| if( **(++argv) != '-' )
475| usage();
476|
477| switch( argv[0][1] )
478| {
479| case 'd': dull = 1; break;
480| default: usage();
481| }
482| }
483| else if( argc != 2 || *argv[1] == '-' )
484| usage();
485|
486| if( !build_tracks( argv[1] ) )
487| exit( 2 );
488|
489| if( stats )
490| {
491| print_stats();
492| exit(0);
493| }
494|
495| signal( SIGINT, on_break );
496|
497| for( speedup(FACTOR, timer_intr); !Done; )
498| {
499| cli(); /* Interrupts off */
500|
501| if( Ring_bell == WARNING )
502| {
503| Collision = 0;
504| Ring_bell = 0;
505| sti();
506| beep ( C4*8, 0.125 );
507| delay( 0.1 );
508| beep ( C4*8, 0.125 );
509| }
510| else if( Collision )
511| {
512| /* the higher track wins */
513|
514| i = max( Collision, Ring_bell );
515| Collision = 0;
516| Ring_bell = 0;
517| sti();
518| beep( dull ? C4*2 : C4 * i, 0.125 );
519| }
520| else if( Ring_bell )
521| {
522| /* You must set Ring_bell to
523| * 0 before calling beep so that
524| * a note won't collide with itself.
525| */
526|
527| i = Ring_bell;
528| Ring_bell = 0;
529| sti();
530| beep( dull ? C4*2 : C4 * i, 0.125 );
531| }
532| else
533| {
534| sti();
535| }
536|
537|
538| if( Downbeat != measure )
539| printf( "\r%d", measure = Downbeat );
540| }
541|
542| printf( "\rDone" );
543|
544| D( printf("%d ticks overall\n", Numticks ); )
545| slowdown();
546| printf("\n");
547| }